﻿namespace EveKillboard.Core
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    using Enums;
    using Interfaces;
    using Services;

    public class KillmailWorker : ITraceSource
    {
        public IEveApiServices ApiServices { get; set; }

        public IItemPriceService PricesProvider { get; set; }

        public IDataAccessProvider DataAccessProvider { get; set; }

        public TraceSource Trace { get; set; }

        /// <summary>
        /// Obtains or defines the number of days before a price is considered obsolete.
        /// Default : 1 day
        /// </summary>
        public int ItemPriceRefreshDelay { get; set; }

        public KillmailWorker( TraceSource traceSource = null )
        {
            Trace = traceSource ?? new TraceSource("KillmailWorker");
            ItemPriceRefreshDelay = 1; /* Default */
        }

        public virtual bool CheckExists(long hash, IFeedKillmail killmail, out Killmail km)
        {
            if(DataAccessProvider.Exists(k => k.Hash == hash, out km))
            {
                Trace.TraceEvent(TraceEventType.Verbose,"Killmail \"{0}\" already exists", km.Id);

                if(!km.EveId.HasValue && killmail.CcpId > 0)
                {
                    km.EveId = killmail.CcpId;
                    km.Updated = DateTime.UtcNow;
                    km = DataAccessProvider.Save(km);

                    Trace.TraceEvent(TraceEventType.Verbose, "Killmail \"{0}\" EveID updated.", km.Id);
                }
                return true;
            }
            return false;
        }

        public virtual bool IsKillmailValid(IFeedKillmail killmail)
        {
            if (string.IsNullOrEmpty(killmail.VictimName) && killmail.VictimId < 1)
                return false;
            if (string.IsNullOrEmpty(killmail.VictimShipName) && killmail.VictimShipId < 1)
                return false;
            if (string.IsNullOrEmpty(killmail.VictimCorporationName) && killmail.VictimCorporationId < 1)
                return false;
            if (string.IsNullOrEmpty(killmail.SystemName) && killmail.SystemId < 1)
                return false;
            if (killmail.DamageReceived < 0)
                return false;
            if (killmail.Involved == null || !killmail.Involved.Any())
                return false;

            return true;
        }

        public virtual Killmail SaveKillmail(IFeedKillmail killmail)
        {
            if (killmail == null) return null;
            if (!IsKillmailValid(killmail))
                throw new Exception("Invalid killmail");

            if (!killmail.CanGetHashKey())
            {
                Trace.TraceEvent(TraceEventType.Verbose, "Retrieving missing values to compute killmail hash key");

                if (string.IsNullOrEmpty(killmail.VictimShipName))
                {
                    Item item = null;
                    if (DataAccessProvider.Exists(i => i.Id == killmail.VictimShipId, out item))
                    {
                        killmail.VictimShipName = item.Name; 
                    }
                    else
                        throw new Exception("Invalid victim ship");
                }

                if (string.IsNullOrEmpty(killmail.VictimName))
                {
                    Character character = null;
                    if (DataAccessProvider.Exists(e => e.EveId == killmail.VictimId, out character))
                    {
                        killmail.VictimName = character.Name; 
                    }
                    else
                    {
                        var charInfos = ApiServices.GetCharacterInfos(killmail.VictimId);
                        if (charInfos == null)
                            throw new Exception(string.Format("Can't get victim with Id: \"{0}\" Character Infos from CCP API", killmail.VictimId));
                        if (string.IsNullOrEmpty(charInfos.CharacterName))
                            throw new Exception(string.Format("Invalid victim with Id: \"{0}\" character name", killmail.VictimId));
                        killmail.VictimName = charInfos.CharacterName;
                    }
                }
                
                if (string.IsNullOrEmpty(killmail.VictimCorporationName))
                {
                    Corporation corp;
                    if (DataAccessProvider.Exists( c => c.EveId == killmail.VictimCorporationId, out corp))
                    {
                        killmail.VictimCorporationName =  corp.Name; 
                    }
                    else if (killmail.VictimCorporationId > 0)
                    {
                        var charInfos = ApiServices.GetCharacterInfos(killmail.VictimId);
                        if (charInfos == null)
                            throw new Exception(string.Format("Can't get victim with Id: \"{0}\" Character Infos from CCP API", killmail.VictimId));
                        if (string.IsNullOrEmpty(charInfos.CorporationName))
                            throw new Exception(string.Format("Invalid victim with Id: \"{0}\" Corporation name", killmail.VictimId));

                        killmail.VictimCorporationName = charInfos.CorporationName;
                    }
                }

                if (string.IsNullOrEmpty(killmail.SystemName))
                {
                    Location location;
                    if (DataAccessProvider.Exists(l => l.Id == killmail.SystemId, out location))
                    {
                        killmail.SystemName = location.System;
                    }
                    else
                        throw new Exception("Invalid killmail location");
                }

                if (string.IsNullOrEmpty(killmail.VictimAllianceName) && killmail.VictimAllianceId > 0)
                {
                    Alliance alliance;
                    if( DataAccessProvider.Exists(a => a.EveId == killmail.VictimAllianceId, out alliance))
                        killmail.VictimAllianceName = alliance.Name;
                    else
                    {
                        var charInfos = ApiServices.GetCharacterInfos(killmail.VictimId);
                        if (charInfos == null)
                            throw new Exception(string.Format("Can't get victim with Id: \"{0}\" Character Infos from CCP API", killmail.VictimId));

                        killmail.VictimAllianceName = charInfos.AllianceName;
                    }
                }
                
                if (string.IsNullOrEmpty(killmail.VictimFactionName) && killmail.VictimFactionId > 0)
                {
                    Faction faction;
                    if (DataAccessProvider.Exists( f => f.Id == killmail.VictimFactionId, out faction))
                        killmail.VictimFactionName = faction.Name; 
                }
                else if (string.IsNullOrEmpty(killmail.VictimFactionName) && killmail.VictimFactionId == 0)
                    killmail.VictimFactionName = "None";
                
                if (!killmail.CanGetHashKey())
                    throw new Exception("Unable to compute killmail hash key");
            }

            Trace.TraceEvent(TraceEventType.Verbose, "Computing killmail hash key");

            var hash = killmail.GetHashKey();
            Killmail km = null;
            if (CheckExists(hash, killmail, out km))
            {
                Trace.TraceEvent(TraceEventType.Verbose, "Killmail \"{0}\" already exists", km.Id);
                return km;
            }

            km = new Killmail()
                         {
                             Date = killmail.Date,
                             Created = DateTime.UtcNow,
                             Damage = killmail.DamageReceived,
                             EveId = killmail.CcpId > 0 ? killmail.CcpId : (int?)null,
                             Year = (Int16)killmail.Date.Year,
                             Month = (Int16)killmail.Date.Month,
                             Week = (Int16)CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(killmail.Date, CultureInfo.InvariantCulture.DateTimeFormat.CalendarWeekRule, CultureInfo.InvariantCulture.DateTimeFormat.FirstDayOfWeek),
                             Hash = hash
                         };

            Trace.TraceEvent(TraceEventType.Verbose, "Reading killmail destroyed item");

            Item destroyed;
            km.Destroyed = killmail.VictimShipId > 0 && DataAccessProvider.Exists(i => i.Id == killmail.VictimShipId, out destroyed)
                               ? destroyed
                               : DataAccessProvider.Exists( i => i.Name == killmail.VictimShipName, out destroyed)
                                     ? destroyed
                                     : null;

            if (km.Destroyed == null)
                throw new Exception("Invalid victim ship");

            Trace.TraceEvent(TraceEventType.Verbose, "Reading killmail location");

            Location loc;
            km.Location = killmail.SystemId > 0 &&  DataAccessProvider.Exists(l => l.Id == killmail.SystemId, out loc)
                              ? loc 
                              :  DataAccessProvider.Exists( l => l.System == killmail.SystemName, out loc)
                                    ? loc
                                    : null;

            if(km.Location == null)
                throw new Exception("Invalid killmail location");

            Trace.TraceEvent(TraceEventType.Verbose, "Reading killmail victim character");
            
            Character victim;
            km.Character = killmail.VictimId > 0 && DataAccessProvider.Exists(e => e.EveId == killmail.VictimId, out victim)
                            ? victim
                            : DataAccessProvider.Exists( e => e.Name == killmail.VictimName , out victim  )
                                  ? victim
                                  : new Character() { EveId = killmail.VictimId > 0 ? killmail.VictimId : (int?)null, Name = killmail.VictimName, IsNpc = false, IsStructure = km.Destroyed.Group.CategoryId != 6 };

            if (km.Character != null && km.Character.EveId.GetValueOrDefault(0) == 0 && killmail.VictimId > 0)
                km.Character.EveId = killmail.VictimId;
            else if (km.Character != null && km.Character.EveId.GetValueOrDefault(0) == 0 && killmail.VictimId == 0)
            {
                var charId = ApiServices.GetCharacterId(km.Character.Name);
                if(charId.GetValueOrDefault(0) > 0)
                    km.Character.EveId = charId;
            }

            if (km.Character != null && km.Character.Id == 0)
            {
                km.Character = DataAccessProvider.Add(km.Character);
                Trace.TraceEvent(TraceEventType.Verbose, "New Character created Id:{0} Name:{1}", km.Character.Id, km.Character.Name);    
            }

            UpdateCharacterCorporation(km.Character, DataAccessProvider, ApiServices);

            Trace.TraceEvent(TraceEventType.Verbose, "Reading killmail victim corporation");
            
            Corporation victimCorp;
            if(killmail.VictimCorporationId > 0 || !string.IsNullOrEmpty(killmail.VictimCorporationName))
                km.Corporation = killmail.VictimCorporationId > 0 &&  DataAccessProvider.Exists( c => c.EveId == killmail.VictimCorporationId, out victimCorp)
                                     ? victimCorp
                                     :  DataAccessProvider.Exists( c => c.Name == killmail.VictimCorporationName, out victimCorp)
                                           ? victimCorp
                                           : new Corporation() { EveId = killmail.VictimCorporationId, Name = killmail.VictimCorporationName };

            if(km.Corporation !=null && km.Corporation.EveId == 0 && killmail.VictimCorporationId > 0)
                km.Corporation.EveId = killmail.VictimCorporationId;
            else if (km.Corporation != null && km.Corporation.EveId == 0 && killmail.VictimCorporationId == 0 && km.Character.EveId.HasValue && km.Character.EveId.Value > 0)
            {
                var charInfos = ApiServices.GetCharacterInfos(km.Character.EveId.Value);
                if(charInfos!=null)
                {
                    foreach (var employment in charInfos.Employments)
                    {
                        var corpInfos = ApiServices.GetCorporationInfos(employment.CorporationId);
                        if (corpInfos == null ||  !string.Equals(corpInfos.CorporationName, km.Corporation.Name, StringComparison.Ordinal)) continue;

                        km.Corporation.EveId = corpInfos.CorporationId;
                        break;
                    }
                }
            }

            if (km.Corporation !=null && km.Corporation.Id == 0)
            {
                km.Corporation = DataAccessProvider.Add(km.Corporation);
                Trace.TraceEvent(TraceEventType.Verbose, "New Corporation created Id:{0} Name:{1}", km.Corporation.Id, km.Corporation.Name);
            }

            UpdateCorporationAlliance(km.Corporation, DataAccessProvider, ApiServices);

            Trace.TraceEvent(TraceEventType.Verbose, "Reading killmail victim alliance");

            Alliance victimAlliance;
            if (killmail.VictimAllianceId > 0 || !string.IsNullOrEmpty(killmail.VictimAllianceName))
                km.Alliance = killmail.VictimAllianceId > 0 && DataAccessProvider.Exists( a => a.EveId == killmail.VictimAllianceId, out victimAlliance)
                                  ? victimAlliance
                                  :  DataAccessProvider.Exists( a => a.Name == killmail.VictimAllianceName, out victimAlliance)
                                        ? victimAlliance
                                        : string.Equals("None", killmail.VictimAllianceName, StringComparison.InvariantCultureIgnoreCase) || string.Equals("Unknown", killmail.VictimAllianceName, StringComparison.InvariantCultureIgnoreCase) 
                                            ? null 
                                            : new Alliance() { EveId = killmail.VictimAllianceId, Name = killmail.VictimAllianceName };

            if(km.Alliance !=null && km.Alliance.EveId == 0 && killmail.VictimAllianceId > 0)
                km.Alliance.EveId = killmail.VictimAllianceId;

            if (km.Alliance != null && km.Alliance.Id == 0)
            {
                km.Alliance = DataAccessProvider.Add(km.Alliance);
                Trace.TraceEvent(TraceEventType.Verbose, "New Alliance created Id:{0} Name:{1}", km.Alliance.Id, km.Alliance.Name);
            }

            Trace.TraceEvent(TraceEventType.Verbose, "Loading killmail victim faction");

            Faction victimFaction;
            if (killmail.VictimFactionId > 0 || !string.IsNullOrEmpty(killmail.VictimFactionName))
                km.Faction = killmail.VictimFactionId > 0 && DataAccessProvider.Exists( f => f.Id == killmail.VictimFactionId, out victimFaction)
                                 ? victimFaction
                                 : DataAccessProvider.Exists( f => f.Name == killmail.VictimFactionName, out victimFaction)
                                       ? victimFaction
                                       : null;

            if (km.Character.LastKnownCorporation != km.Corporation && DateTime.UtcNow.Date.Subtract(km.Date).Days < 8)
            {
                km.Character.LastKnownCorporation = km.Corporation;
                km.Character.Updated = DateTime.UtcNow;

                Trace.TraceEvent(TraceEventType.Verbose, "Character Id:{0} Name:{1} LastKnownCorporation updated", km.Character.Id, km.Character.Name);
            }

            if (km.Corporation.LastKnownAlliance != km.Alliance && DateTime.UtcNow.Date.Subtract(km.Date).Days < 8)
            {
                km.Corporation.LastKnownAlliance = km.Alliance;
                km.Corporation.Updated = DateTime.UtcNow;

                Trace.TraceEvent(TraceEventType.Verbose, "Corporation Id:{0} Name:{1} LastKnownAlliance updated", km.Corporation.Id, km.Corporation.Name);
            }

            AddInvolvedParties(km, killmail);
            AddItems(km, killmail);
            AddItemsValue(km);

            Trace.TraceEvent(TraceEventType.Verbose, "Saving killmail to database");
            
            km  = DataAccessProvider.Add(km);

            Trace.TraceEvent(TraceEventType.Information, "Killmail \"{0}\" saved", km.Id);

            return km;
        }

        private void UpdateCharacterCorporation(Character character, IDataAccessProvider dataAccessProvider, IEveApiServices apiServices)
        {
            if(character ==null || dataAccessProvider ==null || apiServices == null) return;

            if (character.EveId.GetValueOrDefault(0) > 0 && (character.LastKnownCorporation == null || !character.Updated.HasValue || DateTime.UtcNow.Subtract(character.Updated.Value).Days > 7))
            {
                var charInfos = apiServices.GetCharacterInfos(character.EveId.GetValueOrDefault(0));
                if (charInfos != null && charInfos.CorporationId > 0)
                {
                    var corp = dataAccessProvider.Find<Corporation>(c => c.EveId == charInfos.CorporationId);
                    character.LastKnownCorporation = corp ?? new Corporation() { EveId = charInfos.CorporationId, Name = charInfos.CorporationName };
                    character.Updated = DateTime.UtcNow;

                    Trace.TraceEvent(TraceEventType.Verbose, "Character Id:{0} Name:{1} LastKnownCorporation updated", character.Id, character.Name);
                }
            }
        }

        private void UpdateCorporationAlliance(Corporation corporation, IDataAccessProvider dataAccessProvider, IEveApiServices apiServices)
        {
            if(corporation ==null || dataAccessProvider == null || apiServices == null) return;

            if (corporation.EveId.GetValueOrDefault(0) > 0 && (corporation.LastKnownAlliance == null || !corporation.Updated.HasValue || DateTime.UtcNow.Subtract(corporation.Updated.Value).Days > 7))
            {
                var corpInfos = apiServices.GetCorporationInfos(corporation.EveId.GetValueOrDefault(0));
                if (corpInfos != null && corpInfos.AllianceId > 0)
                {
                    var ally = dataAccessProvider.Find<Alliance>(a => a.EveId == corpInfos.AllianceId);
                    corporation.LastKnownAlliance = ally ?? new Alliance() { EveId = corpInfos.AllianceId, Name = corpInfos.AllianceName };
                    corporation.Updated = DateTime.UtcNow;

                    Trace.TraceEvent(TraceEventType.Verbose, "Corporation Id:{0} Name:{1} LastKnownAlliance updated", corporation.Id, corporation.Name);
                }
            }
        }

        private void AddInvolvedParties(Killmail km, IFeedKillmail killmail)
        {
            Trace.TraceEvent(TraceEventType.Verbose, "Adding involved parties");

            km.Involved = new List<Involved>();
            foreach (var killmailInvolved in killmail.Involved)
            {
                if (killmailInvolved == null) continue;

                var current = killmailInvolved;
                var involved = new Involved()
                {
                    Killmail = km,
                    IsFinalBlow = current.IsFinalBlow,
                    Damage = current.DamageDone,
                    Security = current.SecurityStatus
                };

                Item ship;
                involved.Ship = current.ShipId > 0 && DataAccessProvider.Exists( i => i.Id == current.ShipId, out ship)
                                    ? ship
                                    : DataAccessProvider.Exists( i=>i.Name == current.ShipName, out ship)
                                          ? ship
                                          : null;
                Item weapon;
                involved.Weapon = current.WeaponId > 0 && DataAccessProvider.Exists( i => i.Id == current.WeaponId, out weapon)
                                      ? weapon
                                      : DataAccessProvider.Exists(i => i.Name == current.WeaponName, out weapon)
                                            ? weapon
                                            : null;

                if (involved.Ship == null && involved.Weapon == null)
                {
                    Trace.TraceInformation("No ship or weapon found for involved party. Name: {0}, Id: {1}", killmailInvolved.CharacterName, killmailInvolved.CharacterId);
                    continue;
                }

                Corporation corp;
                involved.Corporation = current.CorporationId > 0 && DataAccessProvider.Exists( c => c.EveId == current.CorporationId, out corp )
                                           ? corp
                                           : DataAccessProvider.Exists( c => c.Name == current.CorporationName, out corp)
                                                 ? corp
                                                 : !string.IsNullOrEmpty(current.CorporationName) 
                                                    ? new Corporation() { EveId = current.CorporationId, Name = current.CorporationName }
                                                    : null;

                if(involved.Corporation !=null && involved.Corporation.EveId == 0 && current.CorporationId > 0 )
                    involved.Corporation.EveId = current.CorporationId;

                if (involved.Corporation != null && involved.Corporation.Id == 0)
                    involved.Corporation = DataAccessProvider.Add(involved.Corporation);

                Faction faction;
                if (current.FactionId > 0 || !string.IsNullOrEmpty(current.FactionName))
                    involved.Faction = current.FactionId > 0 && DataAccessProvider.Exists( f => f.Id == current.FactionId, out faction)
                                           ? faction
                                           : DataAccessProvider.Exists( f => f.Name == current.FactionName, out faction)
                                                 ? faction
                                                 : null;
                Alliance alliance;
                if (current.AllianceId > 0 || !string.IsNullOrEmpty(current.AllianceName))
                    involved.Alliance = current.AllianceId > 0 && DataAccessProvider.Exists( a => a.EveId == current.AllianceId, out alliance)
                                            ? alliance 
                                            : DataAccessProvider.Exists( a => a.Name == current.AllianceName, out alliance )
                                                  ? alliance
                                                  : !string.IsNullOrEmpty(current.AllianceName) && !string.Equals("None", current.AllianceName, StringComparison.InvariantCultureIgnoreCase) && !string.Equals("Unknown", current.AllianceName, StringComparison.InvariantCultureIgnoreCase)
                                                    ? new Alliance() { EveId = current.AllianceId, Name = current.AllianceName }
                                                    : null;

                if(involved.Alliance != null && involved.Alliance.EveId == 0 && current.AllianceId > 0)
                    involved.Alliance.EveId = current.AllianceId;

                if (involved.Alliance != null && involved.Alliance.Id == 0)
                    involved.Alliance = DataAccessProvider.Add(involved.Alliance);

                bool isNpc = (involved.Corporation == null && involved.Faction == null && current.CharacterId == 0 && string.IsNullOrEmpty(current.CharacterName)) ||
                             (current.CharacterId == 0 && string.IsNullOrEmpty(current.CharacterName));

                bool isStructure = (involved.Ship != null && involved.Ship.Group.CategoryId != 6 && involved.Ship.Group.CategoryId != 11) || (involved.Ship == null && involved.Weapon != null && (involved.Weapon.Group.CategoryId == 11 /* sentry gun */ || involved.Weapon.Group.CategoryId == 22 /* Mobile warp Disruptor */ || involved.Weapon.Group.CategoryId == 23 /* Control Tower */));

                var name = isNpc
                               ? involved.Corporation != null
                                     ? involved.Corporation.Name
                                     : involved.Faction != null
                                           ? involved.Faction.Name
                                           : involved.Ship != null
                                                 ? involved.Ship.Name
                                                 : involved.Weapon != null
                                                     ? involved.Weapon.Name
                                                     : null
                               : current.CharacterName;

                if (string.IsNullOrEmpty(name))
                    throw new Exception("Invalid involved character name");

                Character attacker;
                var entity = current.CharacterId > 0 && DataAccessProvider.Exists(c => c.EveId == current.CharacterId, out attacker)
                                      ? attacker
                                      : DataAccessProvider.Exists( c => c.Name == name, out attacker )
                                            ? attacker
                                            : new Character() { EveId = current.CharacterId > 0 ? current.CharacterId : (int?)null, Name = name, IsNpc = isNpc, IsStructure = isStructure };

                if (!isNpc && !isStructure && entity != null && entity.EveId.GetValueOrDefault(0) == 0 && current.CharacterId > 0)
                    entity.EveId = current.CharacterId;
                else if (!isNpc && !isStructure && entity != null && entity.EveId.GetValueOrDefault(0) == 0 && current.CharacterId == 0)
                {
                    var charId = ApiServices.GetCharacterId(entity.Name);
                    if(charId.GetValueOrDefault(0) > 0)
                    {
                        entity.EveId = charId.Value;
                    }
                }

                if (entity !=null && entity.Id == 0)
                    entity = DataAccessProvider.Add(entity);

                if (!isNpc && !isStructure && involved.Corporation != null && involved.Corporation.EveId == 0 && current.CorporationId == 0 && entity.EveId.HasValue && entity.EveId.Value > 0)
                {
                    var charInfos = ApiServices.GetCharacterInfos(entity.EveId.Value);
                    if(charInfos!=null)
                    {
                        foreach (var employment in charInfos.Employments)
                        {
                            var corpInfos = ApiServices.GetCorporationInfos(employment.CorporationId);
                            if(corpInfos==null || !string.Equals(corpInfos.CorporationName,involved.Corporation.Name,StringComparison.Ordinal))
                                continue;

                            involved.Corporation.EveId = corpInfos.CorporationId;
                            break;
                        }
                    }
                }

                involved.Character = entity;

                if (involved.Character.LastKnownCorporation != involved.Corporation && involved.Killmail != null && DateTime.UtcNow.Date.Subtract(involved.Killmail.Date).Days < 8)
                {
                    involved.Character.LastKnownCorporation = involved.Corporation;
                    involved.Character.Updated = DateTime.UtcNow;

                    Trace.TraceEvent(TraceEventType.Verbose, "Character Id:{0} Name:{1} LastKnownCorporation updated", involved.Character.Id, involved.Character.Name);
                }
                if (involved.Corporation.LastKnownAlliance != involved.Alliance && involved.Killmail != null && DateTime.UtcNow.Date.Subtract(involved.Killmail.Date).Days < 8)
                {
                    involved.Corporation.LastKnownAlliance = involved.Alliance;
                    involved.Corporation.Updated = DateTime.UtcNow;

                    Trace.TraceEvent(TraceEventType.Verbose, "Corporation Id:{0} Name:{1} LastKnownAlliance updated", involved.Corporation.Id, involved.Corporation.Name);
                }

                UpdateCharacterCorporation(involved.Character, DataAccessProvider, ApiServices);
                UpdateCorporationAlliance(involved.Corporation, DataAccessProvider, ApiServices);

                km.Involved.Add(involved);
                
            }
            var topdamage = km.Involved.OrderByDescending(i => i.Damage).FirstOrDefault();
            if (topdamage != null)
                topdamage.IsTopDamage = true;
        }

        private void AddItems(Killmail km, IFeedKillmail killmail)
        {
            Trace.TraceEvent(TraceEventType.Verbose, "Adding killmail items");

            km.Items = new List<KillmailItem> { new KillmailItem() { Killmail = km, IsShip = true, Item = km.Destroyed, Quantity = 1 } };
            if (killmail.Items.Any())
                foreach (var current in killmail.Items)
                {
                    var kmi = new KillmailItem()
                                  {
                                      IsShip = false,
                                      Killmail = km,
                                      IsDestroyed = current.IsDestroyed,
                                      IsDropped = current.IsDropped,
                                      IsCopy = current.IsCopy,
                                      Cargo = current.IsCargo,
                                      DroneBay = current.IsDroneBay,
                                      Implant = current.IsImplant,
                                      Quantity = current.Quantity,
                                  };

                    var current1 = current;
                    Item item;
                    kmi.Item = current.ItemId > 0 && DataAccessProvider.Exists( i => i.Id == current1.ItemId, out item)
                                   ? item
                                   : DataAccessProvider.Exists( i => i.Name == current1.ItemName, out item)
                                         ? item
                                         : null;
                    
                    ItemName oldName = null;
                    if(item == null && DataAccessProvider.Exists<ItemName>( i => i.Name == current1.ItemName, out oldName))
                    {
                        kmi.Item = DataAccessProvider.Find<Item>(i => i.Id == oldName.ItemId);
                    }
                    
                    if (kmi.Item == null)
                        throw new Exception(string.Format("Invalid killmail item Id:\"{0}\" Name:\"{1}\"", current.ItemId, current.ItemName));

                    // Small fix for items declared has implant which are not implants
                    if (kmi.Implant && kmi.Item.Group.CategoryId != 20)
                        kmi.Implant = false;

                    km.Items.Add(kmi);
                }
        }

        private void AddItemsValue(Killmail km)
        {
            Trace.TraceEvent(TraceEventType.Verbose, "Adding killmail items value");

            km.ItemValues = new List<KillmailItemValue>();
            foreach (var item in km.Items.Where(i => i.Item != null && !i.IsCopy).GroupBy(i => i.Item).Select(g => g.Key))
            {
                var current = item;
                var itemPrice = DataAccessProvider.FindAll<ItemValue>( i => i.ItemId == current.Id, true).OrderBy( i => i.SourceId ).FirstOrDefault();
                if(itemPrice == null && PricesProvider !=null)
                {
                    var price = PricesProvider.GetPrice(current.Id);
                    if(price.HasValue)
                    {
                        itemPrice = new ItemValue() { Item = current, SourceId = (short)ItemValueSourcesEnum.Auto, Update = DateTime.UtcNow, Value = price.Value };

                        Trace.TraceEvent(TraceEventType.Verbose, "Item Id:{0} Name:{1} price created", current.Id, current.Name);
                    }
                }
                else
                    if (itemPrice != null && itemPrice.SourceId != (short)ItemValueSourcesEnum.Manual && DateTime.UtcNow.Subtract(itemPrice.Update).TotalDays > (ItemPriceRefreshDelay <= 0 ? 1 : ItemPriceRefreshDelay) && PricesProvider != null)
                    {
                        var price = PricesProvider.GetPrice(current.Id);
                        if(price.HasValue)
                        {
                            itemPrice.Value = price;
                            itemPrice.Update = DateTime.UtcNow;

                            Trace.TraceEvent(TraceEventType.Verbose, "Item Id:{0} Name:{1} price updated", current.Id, current.Name);

                        }
                    }
                
                if(itemPrice !=null && itemPrice.Value.HasValue)
                {
                    km.ItemValues.Add(new KillmailItemValue()
                                          {
                                              Item = current,
                                              ItemValue = itemPrice.Value.Value,
                                              Killmail = km,
                                              SourceId = itemPrice.SourceId
                                          });
                }
            }
        }

       
        
    }
}